//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Diagnostics.Contracts;
using System.Globalization;

namespace OpenLS.Spreadsheet.Parsing
{
    internal sealed class Numeric : Token
    {
        private readonly string _valueString;
        public readonly string OriginalString;
        public readonly object Value;

        public Numeric(string s, string originalStr)
        {
            Contract.Requires(s != null);
            OriginalString = originalStr;
            _valueString = s;
            char dot = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator[0];
            {
                try
                {
                    Value = double.Parse(s, NumberFormatInfo.InvariantInfo);
                }
                catch (Exception e)
                {
                    throw new Exception("Invalid numeric : " + originalStr + " " + e.Message);
                }
            }
        }

        public Numeric(string s, string originalStr, Type forceType, bool isHexa)
        {
            Contract.Requires(forceType != null);
            OriginalString = originalStr;
            _valueString = s;
            if (forceType == typeof (int))
                Value = (int) LongHexa(s);
            else if (forceType == typeof (uint))
                Value = (uint) LongHexa(s);
            else if (forceType == typeof (long))
                Value = LongHexa(s);
            else if (forceType == typeof (ulong))
                Value = uLongHexa(s);
            else
            {
                throw new Exception("Invalid numeric : " + originalStr);
            }
        }

        public Numeric(string s, string originalStr, bool isHexa)
        {
            OriginalString = originalStr;
            _valueString = s;
            try
            {
                long l;
                l = long.Parse(s, NumberStyles.AllowHexSpecifier, NumberFormatInfo.InvariantInfo);
                if (l < 0)
                    throw new Exception(); // TODO: remove exception and use in a better way
                Value = l;
                if (l <= int.MaxValue && l >= int.MinValue)
                {
                    Value = (int) l;
                    return;
                }
                if (l >= uint.MinValue && l <= uint.MaxValue)
                {
                    Value = (uint) l;
                    return;
                }
            }
            catch
            {
                ulong ul = ulong.Parse(s, NumberStyles.AllowHexSpecifier, NumberFormatInfo.InvariantInfo);
                Value = ul;
            }
        }

        public Numeric(string s, string originalStr, Type forceType)
        {
            Contract.Requires(s != null);
            Contract.Requires(originalStr != null);
            OriginalString = originalStr;
            _valueString = s;
            Contract.Assert(forceType != null);
            try
            {
                if (forceType == typeof (double))
                {
                    Value = double.Parse(s, NumberFormatInfo.InvariantInfo);
                    //Value = System.Globalization.NumberFormatInfo.CurrentInfo.
                    //System.Globalization.NumberFormatInfo
                    return;
                }
                if (forceType == typeof (decimal))
                {
                    Value = decimal.Parse(s, NumberFormatInfo.InvariantInfo);
                    return;
                }
                if (forceType == typeof (float))
                {
                    Value = float.Parse(s, NumberFormatInfo.InvariantInfo);
                    return;
                }
                if (forceType == typeof (int))
                {
                    Value = int.Parse(s, NumberFormatInfo.InvariantInfo);
                    return;
                }
                if (forceType == typeof (long))
                {
                    Value = long.Parse(s, NumberFormatInfo.InvariantInfo);
                    return;
                }
                if (forceType == typeof (uint))
                {
                    Value = uint.Parse(s, NumberFormatInfo.InvariantInfo);
                    return;
                }
                if (forceType == typeof (ulong))
                {
                    Value = ulong.Parse(s, NumberFormatInfo.InvariantInfo);
                    return;
                }
            }
            catch (Exception)
            {
                throw new Exception("Invalid numeric : " + originalStr);
            }
            throw new Exception("Invalid numeric : " + originalStr);
        }

        public override TokenId Type
        {
            get { return TokenId.Numeric; }
        }


        internal int ValueAsInteger
        {
            get { return Convert.ToInt32(Value); }
        }

        public override string ToString()
        {
            if (OriginalString != null)
                return OriginalString;
            return _valueString;
        }

        public static long LongHexa(string s)
        {
            long l = 0;
            long mult = 1;
            int end = 0;
            if (s[0] == '-')
            {
                mult = -1;
                end++;
            }
            //checked //TODO: check
            {
                for (int i = s.Length - 1; i >= end; i--)
                {
                    l += CharTypeManager.HexadecimalValue(s[i])*mult;
                    if (i > 0)
                        mult *= 16;
                }
            }
            return l;
        }

        private static ulong uLongHexa(string s)
        {
            ulong l = 0;
            ulong mult = 1;
            //checked // TODO: check
            {
                for (int i = s.Length - 1; i >= 0; i--)
                {
                    l += (ulong) CharTypeManager.HexadecimalValue(s[i])*mult;
                    if (i > 0)
                        mult *= 16;
                }
            }
            return l;
        }
    }
}